package dviz.tools;

import java.awt.Canvas;
import java.io.File;
import java.io.FileInputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JFrame;

import com.gibbon.jme.context.JmeContext;
import com.gibbon.jme.context.lwjgl.LWJGLContext;
import com.jme.image.Texture;
import com.jme.image.Texture.MagnificationFilter;
import com.jme.image.Texture.MinificationFilter;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Sphere;
import com.jme.util.TextureManager;
import com.jme.util.export.binary.BinaryImporter;
import com.jme.util.export.xml.XMLImporter;
import com.jme.util.resource.ResourceLocatorTool;
import com.jme.util.resource.SimpleResourceLocator;
import com.radakan.jme.mxml.Material;
import com.radakan.jme.mxml.MaterialLoader;
import com.radakan.jme.mxml.OgreLoader;
import com.sun.org.apache.xerces.internal.impl.xs.util.SimpleLocator;

import dviz.visualSystem.impl.ProgressMonitor;

/**
 * @author  zxq071000
 */
public class ModelRepository {
	public String dataFolder;
	static {
		// SimpleResourceLocator locator;
		// locator = new MultiFormatResourceLocator(ModelRepository.class
		// .getClassLoader().getResource("./"), new String[] { ".tga",
		// ".bmp", ".png", ".jpg", ".texture", ".jme" });
		// try {
		// // String urlBase = URLEncoder.encode(System.getProperty("user.dir")
		// // + "/" + DATA_FOLDER);
		// // locator = new MultiFormatResourceLocator(new URL("file", "",
		// // urlBase), new String[] { ".tga", ".bmp", ".png", ".jpg",
		// // ".texture", ".jme" });
		//
		// // System.out.println(System.getProperty("user.dir") + "/"
		// // + DATA_FOLDER);
		// } catch (Exception e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// }
	}

	private static ThreadLocal<ModelRepository> threadModelRepository = new ThreadLocal<ModelRepository>();
	/**
	 * @uml.property  name="modelContext"
	 */
	JmeContext modelContext;
	/**
	 * @uml.property  name="repository"
	 */
	HashMap<String, Object> repository;
	/**
	 * @uml.property  name="loadList"
	 */
	HashMap<String, String> loadList;
	/**
	 * @uml.property  name="monitor"
	 * @uml.associationEnd  
	 */
	ProgressMonitor monitor;
	boolean loaded;
	boolean inited;
	Object loadLock;
	Object initLock;
	{
		loadLock = new Object();
		repository = new HashMap<String, Object>();
		loadList = new HashMap<String, String>();
		loaded = false;
		inited = false;
		initLock = new Object();
	}

	public void stow() {
		threadModelRepository.set(this);
	}

	public static ModelRepository get() {
		return threadModelRepository.get();
	}

	private void initContext() {
		try {
			modelContext = JmeContext.create(LWJGLContext.class,
					JmeContext.CONTEXT_CANVAS);
			modelContext.start();
			Canvas canvas = modelContext.getCanvas();
			JFrame mini = new JFrame();
			mini.getContentPane().add(canvas);
			mini.setUndecorated(true);
			mini.setSize(100, 100);
			mini.setVisible(true);
			modelContext.waitFor();
			mini.setVisible(false);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * @return
	 * @uml.property  name="dataFolder"
	 */
	public String getDataFolder() {
		return dataFolder;
	}

	/**
	 * @param dataFolder
	 * @throws Exception
	 * @uml.property  name="dataFolder"
	 */
	public void setDataFolder(String dataFolder) throws Exception {
		this.dataFolder = dataFolder;
		SimpleResourceLocator locator = new SimpleResourceLocator(ModelRepository.class
				.getClassLoader().getResource(dataFolder));
		ResourceLocatorTool.addResourceLocator(ResourceLocatorTool.TYPE_MODEL,
				locator);
		ResourceLocatorTool.addResourceLocator(
				ResourceLocatorTool.TYPE_TEXTURE, locator);
	}

	public ModelRepository() {
		dataFolder = "";
		try {
			new Thread() {
				String lastKey = null;

				@Override
				public void run() {
					try {
						initContext();
						synchronized (initLock) {
							initLock.notifyAll();
							inited = true;
						}

						XMLImporter xmlImporter = new XMLImporter();
						BinaryImporter binaryImporter = new BinaryImporter();
						while (true) {
							synchronized (loadLock) {
								if (loaded)
									loadLock.wait();
								if (monitor != null)
									monitor.setProgress(0.1f);
								repository.clear();

								// Load all materials into the loader
								URL url = ModelRepository.class
										.getClassLoader().getResource(
												dataFolder);
								File file = new File(url.toURI());
								if (!file.isDirectory())
									throw new RuntimeException(
											"Data folder doesn't exist");

								HashMap<String, Material> materials = new HashMap<String, Material>();
								for (File f : file.listFiles()) {
									if (f.getName().endsWith(".material")
											&& !f.isDirectory()) {
										MaterialLoader loader = new MaterialLoader();
										loader.load(new FileInputStream(f));
										Map<String, Material> aMaterial = loader
												.getMaterials();
										materials.putAll(aMaterial);
									}
								}

								// Load all models which is in the list
								int lc = 0;
								for (String key : loadList.keySet()) {
									try {
										lastKey = key;
										String fn = dataFolder
												+ loadList.get(key);
										Spatial s = null;

										// Handle Ogre Format Only
										if (fn.endsWith("mesh.xml")) {
											OgreLoader loader = new OgreLoader();
											loader.setMaterials(materials);
											s = loader
													.loadModel(ModelRepository.class
															.getClassLoader()
															.getResource(fn));
											s.updateRenderState();
											s.updateGeometricState(0, true);
											s.updateModelBound();
											repository.put(key, s);
										} else if (fn.endsWith(".jpg")) {
											Texture txt = TextureManager
													.loadTexture(
															ModelRepository.class
																	.getClassLoader()
																	.getResource(
																			fn),
															MinificationFilter.BilinearNearestMipMap,
															MagnificationFilter.Bilinear);
											repository.put(key, txt);
										} else {
											throw new RuntimeException(
													"ModelRepository->Load/Run() Format not recognized ->"
															+ key);
										}

										monitor.setProgress(0.1f + 0.9f * lc
												/ loadList.size());
										lc++;
									} catch (Exception e) {
										System.err
												.println("ModelRepository->Run() Error when loading :"
														+ dataFolder
														+ loadList.get(key));
										e.printStackTrace();
										System.exit(0);
									}
								}
								// Add test objects
								Sphere s = new Sphere("", 20, 20, 10);

								repository.put("sphere", s);

								loaded = true;
								loadLock.notifyAll();
								if (monitor != null)
									monitor.setProgress(1f);
							}
						}

					} catch (Exception e) {
						System.err.println("Error at: " + lastKey);
						e.printStackTrace();
					}
				}
			}.start();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * @return
	 * @uml.property name="loadList"
	 */
	public HashMap<String, String> getLoadList() {
		return loadList;
	}

	/**
	 * @param loadList
	 * @uml.property name="loadList"
	 */
	public void setLoadList(HashMap<String, String> loadList) {
		this.loadList = loadList;
	}

	/**
	 * @return
	 * @uml.property name="modelContext"
	 */
	public JmeContext getModelContext() {
		return modelContext;
	}

	/**
	 * @return
	 * @uml.property name="repository"
	 */
	public HashMap<String, Object> getRepository() {
		return repository;
	}

	public void loadModel() {
		synchronized (loadLock) {
			loaded = false;
			loadLock.notifyAll();
		}
	}

	public void waitFor() {
		synchronized (initLock) {
			if (!inited) {
				try {
					initLock.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	public void waitForLoad() {
		synchronized (loadLock) {
			while (!loaded) {
				try {
					loadLock.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * @return
	 * @uml.property name="monitor"
	 */
	public ProgressMonitor getMonitor() {
		return monitor;
	}

	/**
	 * @param monitor
	 * @uml.property name="monitor"
	 */
	public void setMonitor(ProgressMonitor monitor) {
		this.monitor = monitor;
	}
}
